/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012-2014 ForgeRock AS. All Rights Reserved
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://forgerock.org/license/CDDLv1.0.html
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at http://forgerock.org/license/CDDLv1.0.html
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*/
package org.forgerock.openicf.maven;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.security.CodeSource;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.maven.archiver.MavenArchiveConfiguration;
import org.apache.maven.archiver.MavenArchiver;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.resources.remote.BundleRemoteResourcesMojo;
import org.apache.maven.plugin.resources.remote.RemoteResourcesBundle;
import org.apache.maven.plugin.resources.remote.io.xpp3.RemoteResourcesBundleXpp3Writer;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.shared.filtering.MavenFileFilter;
import org.apache.maven.shared.filtering.MavenFilteringException;
import org.apache.maven.shared.filtering.MavenResourcesExecution;
import org.apache.velocity.context.Context;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.jar.JarArchiver;
import org.codehaus.plexus.archiver.jar.ManifestException;
import org.codehaus.plexus.i18n.I18N;
import org.codehaus.plexus.util.DirectoryScanner;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.velocity.VelocityComponent;
import org.identityconnectors.common.l10n.CurrentLocale;
import org.identityconnectors.framework.api.RemoteFrameworkConnectionInfo;
/**
* A DocBookResourceMojo generate the DocBook xml.
* <p/>
* To debug execute this command:
* {@code mvnDebug org.forgerock.maven.plugins:openicf-maven-plugin:docbkx}
*
* @author Laszlo Hordos
*/
@Mojo(name = "docbkx", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true,
requiresDependencyResolution = ResolutionScope.TEST, configurator = "override")
public class DocBookResourceMojo extends AbstractMojo implements ConnectorMojoBridge {
private static final String[] DEFAULT_INCLUDES = new String[] { "**/*.txt", "**/*.vm", };
/**
* The Maven Session Object
*/
@Parameter(property = "session", readonly = true)
protected MavenSession session;
/**
*/
@Parameter(defaultValue = "${project.artifact}", required = true, readonly = true)
private Artifact artifact;
/**
* Encoding of the bundle.
*/
@Parameter(defaultValue = "${project.build.sourceEncoding}")
private String sourceEncoding;
/**
* The current project base directory.
*/
@Parameter(defaultValue = "${basedir}", readonly = true, required = true)
protected File basedir;
/**
* The directory which contains the resources you want packaged up in this
* resource bundle.
*/
@Parameter(defaultValue = "${basedir}/src/main/resources")
private File resourcesDirectory;
/**
* The directory where you want the resource xml-s written to.
*/
@Parameter(defaultValue = "${project.build.outputDirectory}", required = true, readonly = true)
private File buildOutputDirectory;
public File getBuildOutputDirectory() {
return buildOutputDirectory;
}
/**
* Directory that contains the template.
*/
@Parameter(property = "openicf.templateDirectory", defaultValue = "org/forgerock/openicf/maven")
private String templateDirectory;
/**
* The directory which contains the resources you want packaged up in this
* resource bundle.
*/
@Parameter(defaultValue = "${basedir}/src/main/docbkx")
private File docbkxDirectory;
@Parameter(property = "openicf.failOnError", defaultValue = "true")
protected boolean failOnError;
/**
* Specifies the directory where the generated jar file will be put.
*/
@Parameter(property = "project.build.directory")
private String buildDirectory;
/**
* Specifies the filename that will be used for the generated jar file.
* Please note that <code>-docbkx</code> will be appended to the file name.
*/
@Parameter(property = "project.build.finalName")
private String finalName;
/**
* Specifies whether to attach the generated artifact to the project helper. <br/>
*/
@Parameter(property = "attach", defaultValue = "true")
private boolean attach;
/**
* The archive configuration to use. See <a
* href="http://maven.apache.org/shared/maven-archiver/index.html">Maven
* Archiver Reference</a>.
*/
@Parameter
private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
/**
* A list of files to include. Can contain ant-style wildcards and double
* wildcards. The default includes are
* <code>**/*.txt **/*.vm</code>
*/
@Parameter
private String[] includes;
public String[] getIncludes() {
return includes;
}
/**
* A list of files to exclude. Can contain ant-style wildcards and double
* wildcards.
*/
@Parameter
private String[] excludes;
public String[] getExcludes() {
return excludes;
}
/**
* Set this to 'true' to bypass artifact deploy
*
* @since 1.0.0
*/
@Parameter(property = "maven.openicf.skip", defaultValue = "false")
private boolean skip;
@Parameter
private PropertyBag configurationProperties;
public PropertyBag getConfigurationProperties() {
return configurationProperties;
}
@Parameter
private RemoteFrameworkConnectionInfo remoteFrameworkConnectionInfo;
public RemoteFrameworkConnectionInfo getRemoteFrameworkConnectionInfo() {
return remoteFrameworkConnectionInfo;
}
@Parameter(defaultValue = "${mojoExecution}")
MojoExecution execution;
// ----------------------------------------------------------------------
// Mojo components
// ----------------------------------------------------------------------
/**
* Used for attaching the artifact in the project.
*/
@Component
private MavenProjectHelper projectHelper;
/**
* The Jar achiver.
*/
@Component(role = Archiver.class, hint = "jar")
private JarArchiver jarArchiver;
/**
* The Maven Project
*/
@Component
private MavenProject project;
/**
* Velocity Component.
*/
@Component()
private VelocityComponent velocity;
@Component()
private MavenFileFilter fileFilter;
/**
* @plexus.requirement
*/
private I18N i18n;
public File getBasedir() {
return basedir;
}
public String getSourceEncoding() {
return sourceEncoding;
}
public String getTemplateDirectory() {
return templateDirectory;
}
public I18N getI18N() {
return i18n;
}
public MavenProject getMavenProject() {
return project;
}
public void generate(ConnectorDocBuilder builder, Context context, String connectorName)
throws MojoExecutionException {
try {
File rootDirectory =
new File(buildDirectory, "openicf-docbkx/" + getTargetDirectoryName());
Map<String, String> fileSetDictionary = new LinkedHashMap<String, String>();
fileSetDictionary.put("sec-reference.xml.vm", "sec-reference-%s.xml");
fileSetDictionary.put("sec-implemented-interfaces.xml.vm",
"sec-implemented-interfaces-%s.xml");
fileSetDictionary.put("sec-config-properties.xml.vm", "sec-config-properties-%s.xml");
fileSetDictionary.put("sec-schema.xml.vm", "sec-schema-%s.xml");
fileSetDictionary.put("chap-config.xml.vm", "chap-config-%s.xml");
for (Map.Entry<String, String> entry : fileSetDictionary.entrySet()) {
generateDocument(builder, context, connectorName, rootDirectory, entry.getKey());
}
} catch (IOException e) {
getLog().error(e);
throw new MojoExecutionException("Failed to generate DocBook", e);
}
}
protected void generateDocument(ConnectorDocBuilder builder, Context context,
String connectorName, File rootDirectory, String templateFile) throws IOException,
MojoExecutionException {
if (null == context.get("schema") && templateFile.contains("-schema")) {
return;
}
String key =
templateFile
.substring(templateFile.lastIndexOf("/") + 1, templateFile.indexOf('.'));
String fileName = key + "-" + connectorName + ".xml";
File configDoc = new File(rootDirectory, fileName);
FileUtils.mkdir(configDoc.getParentFile().getAbsolutePath());
FileWriter writer = new FileWriter(configDoc);
builder.processTemplate(velocity.getEngine(), context, templateFile, writer);
writer.flush();
writer.close();
context.put(key, fileName);
}
protected String getTargetDirectoryName() {
if (null == execution || execution.getExecutionId().equals("default")) {
return artifact.getArtifactId() + "-" + artifact.getVersion();
} else {
return artifact.getArtifactId() + "-" + artifact.getVersion() + "/"
+ execution.getExecutionId();
}
}
public void execute() throws MojoExecutionException, MojoFailureException {
if (skip) {
getLog().info("Skipping DocBook generation");
return;
}
if (!("pom".equalsIgnoreCase(project.getPackaging()))) {
ArtifactHandler artifactHandler = project.getArtifact().getArtifactHandler();
if (!"java".equals(artifactHandler.getLanguage())) {
getLog().info(
"Not executing DocBook report as the project is not a Java classpath-capable package");
return;
}
}
try {
if (!docbkxDirectory.exists()) {
getLog().info(
"Not executing DocBook report as the project does not have DocBook source");
return;
}
File rootDirectory =
new File(buildDirectory, "openicf-docbkx/" + artifact.getArtifactId() + "-"
+ artifact.getVersion());
try {
FileUtils.mkdir(rootDirectory.getAbsolutePath());
MavenResourcesExecution mre = new MavenResourcesExecution();
mre.setMavenProject(getMavenProject());
mre.setEscapeWindowsPaths(true);
mre.setMavenSession(session);
mre.setInjectProjectBuildFilters(true);
List<FileUtils.FilterWrapper> filterWrappers = null;
try {
filterWrappers = fileFilter.getDefaultFilterWrappers(mre);
} catch (MavenFilteringException e) {
filterWrappers = Collections.emptyList();
}
if (docbkxDirectory.exists()) {
final List<String> includes =
FileUtils.getFileAndDirectoryNames(docbkxDirectory, "**", StringUtils
.join(DirectoryScanner.DEFAULTEXCLUDES, ",")
+ ",**/*.xml", true, false, true, true);
org.apache.commons.io.FileUtils.copyDirectory(docbkxDirectory, rootDirectory,
new FileFilter() {
public boolean accept(File pathname) {
return includes.contains(pathname.getPath());
}
});
List<File> files = FileUtils.getFiles(docbkxDirectory, "**/*.xml", null);
for (File file : files) {
try {
fileFilter.copyFile(file, new File(rootDirectory, file.getName()),
true, filterWrappers, getSourceEncoding());
} catch (MavenFilteringException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}
}
File sharedRoot = rootDirectory.getParentFile();
CodeSource src = getClass().getProtectionDomain().getCodeSource();
if (src != null) {
final ZipInputStream zip = new ZipInputStream(src.getLocation().openStream());
ZipEntry entry = null;
while ((entry = zip.getNextEntry()) != null) {
String name = entry.getName();
if (entry.getName().startsWith("shared")) {
File destination = new File(sharedRoot, name);
if (entry.isDirectory()) {
if (!destination.exists()) {
destination.mkdirs();
}
} else {
if (!destination.exists()) {
FileOutputStream output = null;
try {
output = new FileOutputStream(destination);
IOUtil.copy(zip, output);
} finally {
IOUtil.close(output);
}
}
}
}
}
zip.closeEntry();
zip.close();
}
} catch (IOException e) {
throw new MojoExecutionException("Error copy DocBook resources.", e);
}
// Generate Config and Schema DocBook Chapter
CurrentLocale.set(Locale.ENGLISH);
ConnectorDocBuilder generator = new ConnectorDocBuilder(this);
generator.executeReport();
// Look at the content of the resourcesDirectory and create a
// manifest
// of the files
// so that velocity can easily process any resources inside the JAR
// that
// need to be processed.
if (attach) {
RemoteResourcesBundle remoteResourcesBundle = new RemoteResourcesBundle();
remoteResourcesBundle.setSourceEncoding(getSourceEncoding());
DirectoryScanner scanner = new DirectoryScanner();
scanner.setBasedir(new File(buildDirectory, "openicf-docbkx/"));
// scanner.setIncludes(new String[] { docFolder + "/**" });
scanner.addDefaultExcludes();
scanner.scan();
List<String> includedFiles = Arrays.asList(scanner.getIncludedFiles());
if (resourcesDirectory.exists()) {
scanner = new DirectoryScanner();
scanner.setBasedir(resourcesDirectory);
if (includes != null && includes.length != 0) {
scanner.setIncludes(includes);
} else {
scanner.setIncludes(DEFAULT_INCLUDES);
}
if (excludes != null && excludes.length != 0) {
scanner.setExcludes(excludes);
}
scanner.addDefaultExcludes();
scanner.scan();
includedFiles.addAll(Arrays.asList(scanner.getIncludedFiles()));
}
for (String resource : includedFiles) {
remoteResourcesBundle.addRemoteResource(StringUtils
.replace(resource, '\\', '/'));
}
RemoteResourcesBundleXpp3Writer w = new RemoteResourcesBundleXpp3Writer();
try {
File f =
new File(buildDirectory, "openicf-docbkx/"
+ BundleRemoteResourcesMojo.RESOURCES_MANIFEST);
FileUtils.mkdir(f.getParentFile().getAbsolutePath());
Writer writer = new FileWriter(f);
w.write(writer, remoteResourcesBundle);
} catch (IOException e) {
throw new MojoExecutionException("Error creating remote resources manifest.", e);
}
File outputFile =
generateArchive(new File(buildDirectory, "openicf-docbkx/"), finalName
+ "-docbkx.jar");
projectHelper.attachArtifact(project, "jar", "docbkx", outputFile);
} else {
getLog().info("NOT adding DocBook to attached artifacts list.");
}
} catch (ArchiverException e) {
failOnError("ArchiverException: Error while creating archive", e);
} catch (IOException e) {
failOnError("IOException: Error while creating archive", e);
} catch (RuntimeException e) {
failOnError("RuntimeException: Error while creating archive", e);
}
}
/**
* Method that creates the jar file
*
* @param docbkxFiles
* the directory where the generated jar file will be put
* @param jarFileName
* the filename of the generated jar file
* @return a File object that contains the generated jar file
* @throws ArchiverException
* if any
* @throws IOException
* if any
*/
private File generateArchive(File docbkxFiles, String jarFileName) throws ArchiverException,
IOException {
File docbkxJar = new File(buildDirectory, jarFileName);
if (docbkxJar.exists()) {
docbkxJar.delete();
}
MavenArchiver archiver = new MavenArchiver();
archiver.setArchiver(jarArchiver);
archiver.setOutputFile(docbkxJar);
if (!docbkxFiles.exists()) {
getLog().warn("JAR will be empty - no content was marked for inclusion!");
} else {
archiver.getArchiver().addDirectory(docbkxFiles);
}
List<Resource> resources = project.getBuild().getResources();
for (Resource r : resources) {
if (r.getDirectory().endsWith("maven-shared-archive-resources")) {
archiver.getArchiver().addDirectory(new File(r.getDirectory()));
}
}
try {
archive.setAddMavenDescriptor(false);
archiver.createArchive(session, project, archive);
} catch (ManifestException e) {
throw new ArchiverException("ManifestException: " + e.getMessage(), e);
} catch (DependencyResolutionRequiredException e) {
throw new ArchiverException("DependencyResolutionRequiredException: " + e.getMessage(),
e);
}
return docbkxJar;
}
protected void failOnError(String prefix, Exception e) throws MojoExecutionException {
if (failOnError) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new MojoExecutionException(prefix + ": " + e.getMessage(), e);
}
getLog().error(prefix + ": " + e.getMessage(), e);
}
}